Popular Searches
Popular Course Categories
Popular Courses

setState() in Flutter

Flutter Fundamentals

setState() in Flutter

setState() is one of the most important concepts in Flutter state management. It is used inside a State object to tell Flutter that some internal state has changed and that the widget's UI may need to be rebuilt.

According to the Flutter API documentation, setState() executes the supplied callback synchronously and then schedules the associated widget to rebuild. If state is changed directly without calling setState(), Flutter may not rebuild the UI to reflect that change.


1. Learning Objectives

After completing this topic, you will understand:

  • What setState() means in Flutter.
  • Why setState() is required.
  • How setState() works internally.
  • How to update variables using setState().
  • How to update text, counters, colors, switches, checkboxes, and lists.
  • How setState() works with user interactions.
  • How to use setState() with asynchronous operations.
  • Common mistakes when using setState().
  • Best practices for efficient state updates.

2. What is setState()?

setState() is a method provided by Flutter's State class. It is used when a value stored in a StatefulWidget's state changes and the UI needs to respond to that change.

The basic syntax is:

setState(() {
  // Change state here
});

For example:

int counter = 0;

setState(() {
  counter++;
});

Here, the value of counter changes inside setState(). Flutter is then notified that the widget should be rebuilt.


3. Why Do We Need setState()?

Flutter builds the user interface from the current state of widgets. When a state value changes, Flutter needs to know that the value has changed so that it can rebuild the relevant widget.

For example, suppose we have:

int counter = 0;

If we simply write:

counter++;

the variable changes, but Flutter is not explicitly notified that the UI should rebuild.

Instead, write:

setState(() {
  counter++;
});

Now Flutter knows that the state changed and schedules a rebuild.


4. Basic setState() Flow

The basic flow can be understood as:

  1. User performs an action.
  2. An event handler is executed.
  3. The state variable is changed inside setState().
  4. Flutter is notified that the state changed.
  5. The widget's build() method is scheduled to run again.
  6. The UI is rebuilt using the new state.

Example:

ElevatedButton(
  onPressed: () {
    setState(() {
      counter++;
    });
  },
  child: const Text('Increase'),
)

When the button is pressed, the counter changes and the UI is rebuilt.


5. StatefulWidget and setState()

setState() is normally used inside a class that extends State.

A StatefulWidget generally contains two classes:

  • The StatefulWidget class.
  • The corresponding State class.
class CounterScreen extends StatefulWidget {
  const CounterScreen({super.key});

  @override
  State createState() => _CounterScreenState();
}

class _CounterScreenState extends State {
  int counter = 0;

  @override
  Widget build(BuildContext context) {
    return Text('$counter');
  }
}

The mutable variable counter belongs to the State object, and setState() can be used to notify Flutter when that value changes.


6. Simple Counter Example

The counter example is one of the easiest ways to understand setState().

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: CounterScreen(),
    );
  }
}

class CounterScreen extends StatefulWidget {
  const CounterScreen({super.key});

  @override
  State createState() => _CounterScreenState();
}

class _CounterScreenState extends State {
  int counter = 0;

  void increaseCounter() {
    setState(() {
      counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Counter App'),
      ),
      body: Center(
        child: Text(
          '$counter',
          style: const TextStyle(
            fontSize: 40,
            fontWeight: FontWeight.bold,
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: increaseCounter,
        child: const Icon(Icons.add),
      ),
    );
  }
}

How it works

  • counter stores the current value.
  • The Text widget displays the counter.
  • The floating action button calls increaseCounter().
  • setState() changes counter.
  • Flutter rebuilds the widget with the new value.

7. Changing State Without setState()

Consider this code:

void increaseCounter() {
  counter++;
}

The Dart variable changes, but the widget is not explicitly notified that the UI should update.

The recommended approach is:

void increaseCounter() {
  setState(() {
    counter++;
  });
}

The state change that affects the UI should be performed inside the setState() callback.


8. setState() and the build() Method

When setState() is called, Flutter schedules the State object for rebuilding. During the rebuild, the build() method reads the updated values.

class ExampleScreen extends StatefulWidget {
  const ExampleScreen({super.key});

  @override
  State createState() => _ExampleScreenState();
}

class _ExampleScreenState extends State {
  String message = 'Hello';

  void changeMessage() {
    setState(() {
      message = 'Welcome to Flutter';
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text(message),
        ElevatedButton(
          onPressed: changeMessage,
          child: const Text('Change Message'),
        ),
      ],
    );
  }
}

Initially, the UI displays Hello. After the button is pressed, the state changes and the UI displays Welcome to Flutter.


9. Updating Multiple State Variables

Multiple related state variables can be updated in the same setState() call.

int score = 0;
String status = 'Not Started';

void completeQuiz() {
  setState(() {
    score = 10;
    status = 'Completed';
  });
}

One state update can therefore cause the UI to reflect several related changes.


10. setState() with Boolean Values

Boolean values are frequently used for showing or hiding UI elements.

bool isVisible = false;

void toggleVisibility() {
  setState(() {
    isVisible = !isVisible;
  });
}

Example UI:

Column(
  children: [
    if (isVisible)
      const Text('This text is visible'),

    ElevatedButton(
      onPressed: toggleVisibility,
      child: const Text('Toggle'),
    ),
  ],
)

11. setState() with Switch

bool isDarkMode = false;

Switch(
  value: isDarkMode,
  onChanged: (value) {
    setState(() {
      isDarkMode = value;
    });
  },
)

When the switch changes, the value is updated inside setState().


12. setState() with Checkbox

bool isAccepted = false;

Checkbox(
  value: isAccepted,
  onChanged: (value) {
    setState(() {
      isAccepted = value ?? false;
    });
  },
)

This pattern is useful for terms-and-conditions checkboxes, settings, filters, and selection controls.


13. setState() with TextField

You can use setState() when the text entered by a user needs to immediately affect another part of the UI.

String name = '';

TextField(
  onChanged: (value) {
    setState(() {
      name = value;
    });
  },
)

Text('Hello $name')

As the user types, the name variable changes and the text widget is rebuilt.


14. setState() with a List

Lists are commonly modified using setState().

List items = ['Apple', 'Banana'];

void addItem() {
  setState(() {
    items.add('Orange');
  });
}

Display the list:

ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text(items[index]),
    );
  },
)

When an item is added inside setState(), the UI can rebuild and display the new list contents.


15. Adding and Removing Items

List tasks = [];

void addTask(String task) {
  setState(() {
    tasks.add(task);
  });
}

void removeTask(int index) {
  setState(() {
    tasks.removeAt(index);
  });
}

This pattern can be used for:

  • To-do applications.
  • Shopping carts.
  • Notes applications.
  • Task management systems.
  • Dynamic menus.

16. setState() with Increment and Decrement

int quantity = 1;

void increaseQuantity() {
  setState(() {
    quantity++;
  });
}

void decreaseQuantity() {
  if (quantity > 1) {
    setState(() {
      quantity--;
    });
  }
}

This is useful in shopping cart interfaces.


17. Complete Shopping Cart Example

class ProductScreen extends StatefulWidget {
  const ProductScreen({super.key});

  @override
  State createState() => _ProductScreenState();
}

class _ProductScreenState extends State {
  int quantity = 1;
  final double price = 499;

  void increaseQuantity() {
    setState(() {
      quantity++;
    });
  }

  void decreaseQuantity() {
    if (quantity > 1) {
      setState(() {
        quantity--;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    final double total = price * quantity;

    return Scaffold(
      appBar: AppBar(
        title: const Text('Shopping Cart'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              'Product Price: ₹$price',
              style: const TextStyle(fontSize: 20),
            ),
            const SizedBox(height: 20),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                IconButton(
                  onPressed: decreaseQuantity,
                  icon: const Icon(Icons.remove),
                ),
                Text(
                  '$quantity',
                  style: const TextStyle(fontSize: 24),
                ),
                IconButton(
                  onPressed: increaseQuantity,
                  icon: const Icon(Icons.add),
                ),
              ],
            ),
            const SizedBox(height: 20),
            Text(
              'Total: ₹$total',
              style: const TextStyle(
                fontSize: 24,
                fontWeight: FontWeight.bold,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Important concept

The total value is calculated during build() using the latest quantity. Whenever quantity changes through setState(), Flutter rebuilds the widget and calculates the new total.


18. setState() with Color Changes

Color boxColor = Colors.blue;

void changeColor() {
  setState(() {
    boxColor = Colors.green;
  });
}

Use the state variable in the UI:

Container(
  width: 150,
  height: 150,
  color: boxColor,
)

When changeColor() is called, the container is rebuilt using the new color.


19. setState() with Conditional UI

setState() is frequently used when the UI should change based on a condition.

bool isLoggedIn = false;

void login() {
  setState(() {
    isLoggedIn = true;
  });
}

UI:

isLoggedIn
    ? const Text('Welcome User')
    : ElevatedButton(
        onPressed: login,
        child: const Text('Login'),
      )

When isLoggedIn becomes true, Flutter rebuilds the widget and displays the appropriate UI.


20. setState() with Loading State

A common real-world use of setState() is displaying loading indicators while an asynchronous operation is running.

bool isLoading = false;

Future loadData() async {
  setState(() {
    isLoading = true;
  });

  await Future.delayed(const Duration(seconds: 2));

  if (!mounted) return;

  setState(() {
    isLoading = false;
  });
}

UI example:

isLoading
    ? const CircularProgressIndicator()
    : ElevatedButton(
        onPressed: loadData,
        child: const Text('Load Data'),
      )

The asynchronous work is performed outside the setState() callback. Only the synchronous state changes are placed inside setState().


21. Important Rule: Do Not Make setState() Async

The callback passed to setState() should not be asynchronous.

Incorrect:

setState(() async {
  await Future.delayed(const Duration(seconds: 1));
  counter++;
});

Correct:

Future updateCounter() async {
  await Future.delayed(const Duration(seconds: 1));

  if (!mounted) return;

  setState(() {
    counter++;
  });
}

The Flutter API specifies that the callback passed to setState() must not return a Future.


22. setState() with API Calls

When working with APIs, a common pattern is:

  1. Set loading state to true.
  2. Perform the API request.
  3. Store the result.
  4. Set loading state to false.
  5. Display the updated data.
bool isLoading = false;
String data = '';

Future fetchData() async {
  setState(() {
    isLoading = true;
  });

  await Future.delayed(const Duration(seconds: 2));

  final result = 'Data loaded successfully';

  if (!mounted) return;

  setState(() {
    data = result;
    isLoading = false;
  });
}

23. What is mounted?

The mounted property indicates whether the State object is currently associated with an element in the widget tree.

It is particularly useful after asynchronous operations.

Future loadData() async {
  await Future.delayed(const Duration(seconds: 2));

  if (!mounted) {
    return;
  }

  setState(() {
    data = 'Loaded';
  });
}

This prevents attempting to call setState() on a State object that has already been disposed.


24. setState() After dispose()

Calling setState() after a State object has been disposed is an error.

This can happen when:

  • A timer continues running after the screen is closed.
  • An asynchronous operation completes after the widget is removed.
  • An animation callback continues after disposal.
  • A stream subscription continues after disposal.

Whenever possible, cancel the work that can trigger the update during dispose(). Checking mounted can also protect an update after asynchronous work.


25. setState() and Timer

Timer? timer;
int seconds = 0;

@override
void initState() {
  super.initState();

  timer = Timer.periodic(
    const Duration(seconds: 1),
    (_) {
      if (!mounted) return;

      setState(() {
        seconds++;
      });
    },
  );
}

@override
void dispose() {
  timer?.cancel();
  super.dispose();
}

Remember to import:

import 'dart:async';

26. Updating State Before and After an Operation

For a loading operation, state changes can be separated from the actual work.

Future submitForm() async {
  setState(() {
    isLoading = true;
  });

  final result = await submitData();

  if (!mounted) return;

  setState(() {
    isLoading = false;
    message = result;
  });
}

This structure keeps the setState() callbacks small and focused on actual state changes.


27. Keep setState() Small

It is good practice to place only the state-changing operation inside setState().

Instead of:

setState(() {
  performLargeCalculation();
  saveData();
  updateCounter();
  counter++;
});

Prefer:

performLargeCalculation();
saveData();

setState(() {
  counter++;
});

This makes the code easier to understand and keeps the purpose of setState() clear.


28. setState() Should Only Be Used When UI Needs to React

If a value changes but that change does not affect the widget's output, rebuilding the UI may not be necessary.

For example:

int calculationResult = 0;

void calculate() {
  calculationResult = 100;
}

If calculationResult is not displayed or used by the widget's build logic, calling setState() may be unnecessary.


29. setState() and Performance

Calling setState() schedules a rebuild of the associated State object and potentially its descendant subtree. Therefore, state updates should be kept meaningful and reasonably localized.

Good practices include:

  • Call setState() only when the UI needs to react.
  • Avoid redundant calls.
  • Keep frequently changing state close to the UI that uses it when practical.
  • Split large widgets into smaller widgets.
  • Use const constructors where possible.
  • Avoid putting expensive calculations directly inside frequently executed build methods.

30. Multiple setState() Calls

It is usually cleaner to combine related state changes.

Instead of:

setState(() {
  firstName = 'John';
});

setState(() {
  lastName = 'Smith';
});

You can often write:

setState(() {
  firstName = 'John';
  lastName = 'Smith';
});

This communicates that the changes belong to the same UI update.


31. setState() and User Interaction

Many Flutter widgets provide callbacks where setState() can be used.

Widget Common Callback Example State
ElevatedButton onPressed Counter, form submission
Switch onChanged Enable/disable setting
Checkbox onChanged Selection
Slider onChanged Numeric value
TextField onChanged Input text
DropdownButton onChanged Selected option

32. Slider Example

double volume = 50;

Slider(
  value: volume,
  min: 0,
  max: 100,
  onChanged: (value) {
    setState(() {
      volume = value;
    });
  },
)

The slider value changes continuously, and the UI can react to the updated state.


33. Dropdown Example

String selectedCity = 'Mumbai';

DropdownButton(
  value: selectedCity,
  items: const [
    DropdownMenuItem(
      value: 'Mumbai',
      child: Text('Mumbai'),
    ),
    DropdownMenuItem(
      value: 'Delhi',
      child: Text('Delhi'),
    ),
    DropdownMenuItem(
      value: 'Pune',
      child: Text('Pune'),
    ),
  ],
  onChanged: (value) {
    if (value == null) return;

    setState(() {
      selectedCity = value;
    });
  },
)

34. Favorite Button Example

bool isFavorite = false;

IconButton(
  onPressed: () {
    setState(() {
      isFavorite = !isFavorite;
    });
  },
  icon: Icon(
    isFavorite
        ? Icons.favorite
        : Icons.favorite_border,
  ),
)

This is a simple example of changing a boolean state and dynamically changing an icon.


35. Password Visibility Example

bool obscurePassword = true;

TextField(
  obscureText: obscurePassword,
  decoration: InputDecoration(
    labelText: 'Password',
    suffixIcon: IconButton(
      icon: Icon(
        obscurePassword
            ? Icons.visibility
            : Icons.visibility_off,
      ),
      onPressed: () {
        setState(() {
          obscurePassword = !obscurePassword;
        });
      },
    ),
  ),
)

This pattern is commonly used in login and registration screens.


36. Complete Login UI Example

class LoginScreen extends StatefulWidget {
  const LoginScreen({super.key});

  @override
  State createState() => _LoginScreenState();
}

class _LoginScreenState extends State {
  bool obscurePassword = true;
  bool isLoading = false;

  Future login() async {
    setState(() {
      isLoading = true;
    });

    await Future.delayed(const Duration(seconds: 2));

    if (!mounted) return;

    setState(() {
      isLoading = false;
    });

    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(
        content: Text('Login completed'),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Login'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(20),
        child: Column(
          children: [
            TextField(
              decoration: const InputDecoration(
                labelText: 'Email',
              ),
            ),
            const SizedBox(height: 16),
            TextField(
              obscureText: obscurePassword,
              decoration: InputDecoration(
                labelText: 'Password',
                suffixIcon: IconButton(
                  icon: Icon(
                    obscurePassword
                        ? Icons.visibility
                        : Icons.visibility_off,
                  ),
                  onPressed: () {
                    setState(() {
                      obscurePassword = !obscurePassword;
                    });
                  },
                ),
              ),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: isLoading ? null : login,
              child: isLoading
                  ? const CircularProgressIndicator()
                  : const Text('Login'),
            ),
          ],
        ),
      ),
    );
  }
}

37. Common Mistakes with setState()

Mistake 1: Changing UI State Without setState()

counter++;

When the changed value affects the UI, use:

setState(() {
  counter++;
});

Mistake 2: Making setState() Callback Async

setState(() async {
  await someFunction();
});

Instead, perform asynchronous work outside setState() and update the state synchronously afterward.

Mistake 3: Calling setState() After dispose()

Async callbacks, timers, animations, and subscriptions should be handled carefully so they do not update a disposed State object.

Mistake 4: Calling setState() Unnecessarily

Do not use setState() simply because a value changed. Use it when the change needs to affect the widget's UI.

Mistake 5: Performing Heavy Work Inside setState()

Keep expensive calculations, network requests, and database operations outside the setState() callback.


38. Correct Pattern for Async State Updates

Future fetchUser() async {
  setState(() {
    isLoading = true;
  });

  final user = await getUser();

  if (!mounted) return;

  setState(() {
    username = user.name;
    isLoading = false;
  });
}

The important idea is that setState() contains only the synchronous state modifications.


39. setState() vs Direct Variable Assignment

Direct Assignment setState()
Changes the Dart variable. Changes the state and notifies Flutter.
Does not itself schedule a widget rebuild. Schedules the State object for rebuilding.
Useful when UI does not need to react. Useful when UI needs to reflect the state change.

40. setState() vs StatefulWidget

StatefulWidget provides a structure for widgets that have mutable state, while setState() tells Flutter that a change in that State may require the UI to rebuild.

StatefulWidget
      ↓
State object
      ↓
State variables
      ↓
setState()
      ↓
build()
      ↓
Updated UI

41. setState() and Widget Tree

Suppose the widget tree contains:

Scaffold
 ├── AppBar
 └── Column
      ├── Text
      └── ElevatedButton

If the State object associated with the screen calls setState(), Flutter schedules that State for rebuilding. The build method produces the updated widget configuration for the relevant subtree.


42. Example: Simple Score Application

class ScoreScreen extends StatefulWidget {
  const ScoreScreen({super.key});

  @override
  State createState() => _ScoreScreenState();
}

class _ScoreScreenState extends State {
  int score = 0;

  void addPoint() {
    setState(() {
      score++;
    });
  }

  void resetScore() {
    setState(() {
      score = 0;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Score App'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              'Score: $score',
              style: const TextStyle(fontSize: 30),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: addPoint,
              child: const Text('Add Point'),
            ),
            ElevatedButton(
              onPressed: resetScore,
              child: const Text('Reset'),
            ),
          ],
        ),
      ),
    );
  }
}

43. Local State and setState()

setState() is particularly suitable for small, local pieces of state that belong to one widget.

Examples include:

  • Whether a password is visible.
  • Whether a switch is enabled.
  • Current counter value.
  • Selected tab.
  • Selected dropdown value.
  • Temporary form UI state.
  • Loading indicator state.

44. When setState() May Not Be Enough

For small local state, setState() is often straightforward. Larger applications may need more structured state-management approaches when state must be shared across many unrelated widgets or features.

Common approaches in Flutter projects include:

  • Provider
  • Riverpod
  • Bloc/Cubit
  • GetX
  • Other application-specific state-management architectures

The appropriate approach depends on application size, architecture, state-sharing requirements, and team preferences.


45. setState() Best Practices

  • Use setState() when a state change affects the UI.
  • Keep the callback short and focused.
  • Do not make the callback async.
  • Perform API calls and other asynchronous work outside the callback.
  • Check mounted before updating state after asynchronous work when necessary.
  • Cancel timers, subscriptions, and other ongoing work in dispose() where appropriate.
  • Avoid redundant calls to setState().
  • Keep state close to the widgets that use it when practical.
  • Use const widgets where possible.
  • Split large widgets into smaller components when appropriate.

46. Real-World Example: To-Do App State

class TodoScreen extends StatefulWidget {
  const TodoScreen({super.key});

  @override
  State createState() => _TodoScreenState();
}

class _TodoScreenState extends State {
  final List tasks = [];

  void addTask(String task) {
    if (task.trim().isEmpty) return;

    setState(() {
      tasks.add(task);
    });
  }

  void deleteTask(int index) {
    setState(() {
      tasks.removeAt(index);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Todo App'),
      ),
      body: ListView.builder(
        itemCount: tasks.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(tasks[index]),
            trailing: IconButton(
              icon: const Icon(Icons.delete),
              onPressed: () {
                deleteTask(index);
              },
            ),
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          addTask('Learn Flutter');
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}

Here, adding and deleting tasks are state changes. Both operations use setState() so the task list can be rebuilt with the latest data.


47. Understanding the Complete State Update Cycle

User Action
     ↓
Event Callback
     ↓
setState()
     ↓
State Variable Changes
     ↓
Flutter Schedules Build
     ↓
build() Executes
     ↓
New Widget Configuration
     ↓
Updated UI

This cycle is fundamental to understanding interactive Flutter applications.


48. Important API Facts

  • setState() is a method of the State class.
  • Its callback is executed synchronously.
  • The callback must not return a Future.
  • Calling it tells Flutter that internal state has changed and the widget may need to rebuild.
  • Calling it after dispose() is an error.
  • Redundant calls should be avoided because rebuilding a widget subtree has an indirect performance cost.

49. Official Flutter Resources


50. Flutter Training Resource

For structured Flutter learning covering Dart, widgets, state management, APIs, Firebase, projects, and other Flutter development topics, visit the following course resource:

JustAcademy Flutter Training

For course/demo registration:

Register for Flutter Course Demo


51. Interview Questions on setState()

Q1. What is setState() in Flutter?

setState() is a method of the State class used to notify Flutter that internal state has changed and that the widget may need to rebuild.

Q2. Why is setState() required?

It tells Flutter that a state change may affect the UI and schedules the associated State object for rebuilding.

Q3. Can setState() be async?

No. The callback passed to setState() must not return a Future. Perform asynchronous work outside the callback and update the state synchronously afterward.

Q4. What happens when setState() is called?

The callback is executed synchronously, the State object is marked for rebuilding, and Flutter later calls the relevant build process to update the UI.

Q5. Can setState() be called after dispose()?

No. Calling setState() after disposal is an error.

Q6. What is mounted?

mounted indicates whether the State object is currently associated with an element in the widget tree.

Q7. Can multiple variables be changed inside one setState()?

Yes. Related state changes can be grouped inside the same setState() callback.

Q8. Is setState() a global state-management solution?

No. It is primarily a mechanism for notifying Flutter about state changes in a particular State object. Larger applications may use additional state-management approaches for shared or complex state.


52. Practice Exercises

  1. Create a counter app with Increase, Decrease, and Reset buttons.
  2. Create a dark-mode switch using setState().
  3. Create a password field with show/hide functionality.
  4. Create a shopping cart with quantity increase and decrease buttons.
  5. Create a to-do list where users can add and delete tasks.
  6. Create a checkbox-based terms and conditions UI.
  7. Create a slider that displays its current numeric value.
  8. Create a loading button that displays a progress indicator during an asynchronous operation.
  9. Create a favorite button that changes between favorite and unfavorite icons.
  10. Create a dropdown that updates the selected city on the screen.

53. Quick Revision

Concept Meaning
State Information that can change during a widget's lifetime.
StatefulWidget A widget whose associated State can change during its lifetime.
State Object that stores mutable state and contains the build logic.
setState() Notifies Flutter that the State has changed and may need rebuilding.
build() Produces the widget configuration based on the current state.
mounted Indicates whether the State is currently mounted in the widget tree.
dispose() Used for cleanup when the State is permanently removed.

54. Key Takeaways

  • setState() is fundamental for managing simple local state in Flutter.
  • State changes that affect the UI should generally be performed inside setState().
  • Calling setState() notifies Flutter that the associated State may need to rebuild.
  • The callback passed to setState() should be synchronous.
  • Never put an await operation directly inside the setState() callback.
  • For asynchronous operations, perform the work first and then update state inside a synchronous setState() call.
  • Use mounted when necessary to avoid updating a State object after it has been removed from the widget tree.
  • Cancel timers, subscriptions, and other ongoing work during dispose() when appropriate.
  • Avoid unnecessary setState() calls because they can cause additional widget rebuilding.
  • For larger applications, more structured state-management solutions may be appropriate.

Conclusion

setState() is a core Flutter mechanism for creating interactive user interfaces. Whenever a StatefulWidget's local state changes and that change should be reflected in the UI, setState() provides the notification Flutter needs to rebuild the relevant widget. Understanding the relationship between state variables, setState(), and build() is essential before moving on to advanced state-management approaches such as Provider, Riverpod, Bloc, or GetX.

whatsapp